package cn.chinaunicom.mini.program.dingtalk.events.publisher;

import cn.chinaunicom.mini.program.dingtalk.annotation.DTEvent;
import cn.chinaunicom.mini.program.dingtalk.events.event.DingTalkEvent;
import cn.chinaunicom.mini.program.dingtalk.events.event.log.DingTalkEventLogEvent;
import com.alibaba.fastjson2.JSON;
import com.dingtalk.open.app.api.message.GenericOpenDingTalkEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.Objects;

/**
 * @ProjectName: dingtalk-property
 * @Package: cn.chinaunicom.property.events.publisher
 * @ClassName: EventProcessor
 * @Description: 钉钉事件处理器
 * @Author: wangjc
 * @CreateDate: 2024/6/7
 * @UpdateUser: wangjc
 * @UpdateDate: 2024/6/5
 * @UpdateRemark: 更新内容
 * @Version: 1.0
 */
@Slf4j
@Component
public class EventProcessor {
    /**
     * 钉钉事件类型，用于根据事件类型来决定如何处理这个事件。
     */
    private static Map<String, Class<? extends DingTalkEvent>> eventTypes;

    @Autowired
    private ApplicationEventPublisher publisher;

    @Autowired
    private EventTypeScanner eventTypeScanner;

    /**
     * 在组件初始化后调用此方法，用于扫描并初始化事件类型。
     * 通过指定包名和基类，扫描指定包下所有继承自DTEvent的类，将其作为事件类型进行初始化。
     * 这样做的目的是为了在系统运行前动态地识别和注册所有可能的事件处理类，以便系统能够正确地处理接收到的各种事件。
     *
     * @see EventTypeScanner#scanEventTypeClasses(String, Class)
     */
    @PostConstruct
    public void initEventTypes() {
        eventTypes = eventTypeScanner.scanEventTypeClasses("cn.chinaunicom.mini.program.dingtalk.events.event", DTEvent.class);
    }


    /**
     * 处理钉钉开放平台的事件。
     *
     * 该方法接收一个钉钉推送事件和一个通用的钉钉事件对象，根据事件类型来决定如何处理这个事件。
     * 如果找不到对应的事件类型，则记录错误并返回。如果找到对应的事件类型，则创建该事件类型的实例并发布这个事件。
     *
     * @param dingTalkPublisher 用于发布事件的钉钉发布者对象。
     * @param event 收到的钉钉开放平台事件对象。
     */
    public void processEvent(DingTalkPublisher dingTalkPublisher, GenericOpenDingTalkEvent event) {
        // 获取事件类型
        String eventType = event.getEventType();
        try {
            // 检查事件类型是否有效，如果无效则记录错误并返回
            if (Objects.isNull(eventType) || Objects.isNull(eventTypes.get(eventType))) {
                log.error("未找到对应的事件类型: {} event: {}", eventType, sanitizeLog(event));
                return;
            }

            // 根据事件类型获取对应的事件类
            Class<? extends DingTalkEvent> eventClass = eventTypes.get(eventType);
            // 创建事件类型的实例，并传入钉钉发布者和通用钉钉事件对象
            DingTalkEvent toPublish = eventClass.getConstructor(Object.class, GenericOpenDingTalkEvent.class).newInstance(dingTalkPublisher, event);
            // 发布这个事件为自定义事件
            publisher.publishEvent(toPublish);
        } catch (Exception e) {
            // 记录处理事件时发生的异常
            // 对于所有捕获的异常，记录错误信息
            log.error("处理事件时发生异常: eventType={}, event={}, error={}", eventType, sanitizeLog(event), e.getMessage());
        } finally {
            // 增加钉钉日志事件
            DingTalkEventLogEvent logEvent = new DingTalkEventLogEvent(dingTalkPublisher, event);
            publisher.publishEvent(logEvent);
        }
    }

    // 假设的脱敏方法
    private String sanitizeLog(Object event) {
        // 在这里实现敏感信息的脱敏逻辑
        return JSON.toJSONString(event);
    }
}
